import logging
import os
import re
import time
import traceback

import colorful
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image


global_cnt = 0

class EPC_orderer():
    def __init__(self,config:dict):
        self.username = config['username']
        self.password = config['password']
        flags = dict()
        for key in config:
            if key[:5] == 'flag.':
                value = config[key]
                flags[value] = key[5:]
        self.search_pages = [flags[v] for v in config['search_pages']]
        urls = dict()
        urls['root'] = 'http://epc.ustc.edu.cn'
        urls['main'] = 'http://epc.ustc.edu.cn/main.asp'
        urls['oral.situational'] = 'http://epc.ustc.edu.cn/m_practice.asp?second_id=2001'
        urls['oral.topical'] = 'http://epc.ustc.edu.cn/m_practice.asp?second_id=2002'
        urls['oral.debate'] = 'http://epc.ustc.edu.cn/m_practice.asp?second_id=2003'
        urls['oral.drama'] = 'http://epc.ustc.edu.cn/m_practice.asp?second_id=2004'
        urls['oral.pronunciation'] = 'http://epc.ustc.edu.cn/m_practice.asp?second_id=2007'
        urls['self.comprehensive'] = 'http://epc.ustc.edu.cn/m_service.asp?second_id=3001'
        urls['self.Q&A'] = 'http://epc.ustc.edu.cn/m_service.asp?second_id=3002'
        urls['self.test'] = 'http://epc.ustc.edu.cn/m_service.asp?second_id=3003'
        self.urls = urls
        self.search_urls = [urls[v] for v in self.search_pages]

        self.booked_page = 'http://epc.ustc.edu.cn/record_book.asp'

        global global_cnt
        self.logger = logging.getLogger("EPC_order_{}".format(global_cnt))
        formatter= logging.Formatter(r'[TIME:%(asctime)s][FILE:%(filename)s][LINENO:%(lineno)d] %(message)s')
        self.logger.setLevel(logging.DEBUG)
        streamer = logging.StreamHandler()
        streamer.setLevel(logging.DEBUG)
        streamer.setFormatter(formatter)
        self.logger.addHandler(streamer)
        self.browser = webdriver.Chrome()
        self.wait_time=10

    def ocr_digit(self, im):
        def get_score(im, d):
            im2 = Image.open(r"digits/{}.png".format(d))
            assert(im.size == im2.size)
            x,y = im.size
            ans = 0
            for i in range(x):
                for j in range(y):
                    if im2.getpixel((i,j)) == im.getpixel((i,j)):
                        ans+=1
                    else:
                        ans-=1
            return ans
        ans = 0
        max_score = -9999
        for i in range(10):
            score = get_score(im, i)
            if score > max_score:
                max_score = score
                ans = i 
        return ans

    def get_info_of_class(self, page_source):
        p = re.sub(r">\s+<", "><", page_source)
        def split_time(t):
            p = t.find("<br>")
            assert(p>=0)
            day = t[:p].strip()
            min = t[p+4:].strip()
            return day,min

        pattern = r'<tr><td align="left"><a(.*?)>(?P<title>.*?)</a></td>' + \
            r'<td align="center">(?P<week>.*?)</td>' + \
            r'<td align="center">(?P<weekday>.*?)</td>' + \
            r'<td align="center">(?P<teacher>.*?)</td>' + \
            r'<td align="center">(?P<class_hour>.*?)</td>' + \
            r'<td align="center">(?P<study_time>.*?)</td>' + \
            r'<td align="center">(?P<classroom>.*?)</td>' + \
            r'<td align="center">(?P<book_start_time>.*?)</td>' + \
            r'<td align="center">(?P<book_cancel_time>.*?)</td>' + \
            r'<td align="center">(?P<max_students>.*?)</td>' + \
            r'<td align="center">(?P<current_students>.*?)</td>' + \
            r'<td align="center">(?P<courseware>.*?)</td>' + \
            r'<td align="center">(?P<status>.*?)</td>' + \
            r'</tr>'
        ans = []
        while True:
            ret = re.search(pattern, p, re.S)
            if ret!=None:

                d = ret.groupdict()
                d['study_day'],d['study_min'] = split_time(d['study_time'])
                if d['status'].find('value="预 约"')>=0:
                    ans.append(d)
                p = p[ret.end():]
            else:
                break
        return ans

    def look_up_book_history(self):
        b = self.browser
        b.get(self.booked_page)
        WebDriverWait(b,self.wait_time).until(lambda b:b.page_source.find('预约记录表')>=0)
        titles = b.find_elements_by_xpath(r'/html/body/table[1]/tbody/tr/td/table[2]/tbody/tr/td[2]')
        study_times = b.find_elements_by_xpath(r'/html/body/table[1]/tbody/tr/td/table[2]/tbody/tr/td[8]')
        for i in range(1,len(titles)):
            title = titles[i]
            study_time = study_times[i].text
            study_time = study_time.replace('\n',' ')
            study_time = study_time.replace('\r','')
            study_time = study_time.replace('<br>',' ')
            self.logger.info(colorful.green("【单元：{}】【时间：{}】".format(title.text, study_time)))
        return len(titles)-1



    def qiangke(self):
        b = self.browser
        for page in self.search_pages:
            self.logger.info("正在查询页面：{}".format(page))
            page = self.urls[page]
            b.get(page)
            self.logger.debug("等待出现预约课程")
            WebDriverWait(b,self.wait_time).until(lambda b:b.page_source.find("只显示可预约课程")>=0)
            self.logger.debug("检测到已经出现！")

            exclude_time_list = ['08:25-09:15']
            exclude_day_list=[]

            bookable_list = self.get_info_of_class(b.page_source)
            yuyues = b.find_elements_by_xpath("//input[@value='预 约']")
            satisifyint_list = []
            for i,v in enumerate(bookable_list):
                if v['study_min'] not in exclude_time_list and v['study_day'] not in exclude_day_list:
                    satisifyint_list.append(i)
            print("共有{}个预约，其中有{}个符合时间要求".format(len(yuyues),len(satisifyint_list)))
            if satisifyint_list!=[]:
                i = satisifyint_list[0]
                yuyues[i].click()
                diag = b.switch_to.alert
                diag.accept()
                return True
        return False
    def login(self):
        b = self.browser
        b.get(self.urls['main'])
        menu_frame = b.find_element_by_xpath("/html/body/table/tbody/tr[2]/td[1]/iframe")
        menu_frame_x = menu_frame.location['x']
        menu_frame_y = menu_frame.location['y']
        b.switch_to.frame(menu_frame)
        self.logger.debug("wait for input_name")
        WebDriverWait(b,self.wait_time).until(lambda b: b.find_element_by_xpath('//input[@name="name"]'))
        self.logger.debug("found input_name or time exceeds!")
        input_user_id = b.find_element_by_xpath('//input[@name="name"]')
        input_password = b.find_element_by_xpath('//input[@name="pass"]')
        input_checkcodde = b.find_element_by_xpath('//input[@name="txt_check"]')
        checkcode = b.find_element_by_xpath('/html/body/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td[2]/img')
        input_user_id.send_keys(self.username)
        input_password.send_keys(self.password)
        left = int(menu_frame_x+checkcode.location['x'])
        top = int(menu_frame_y+checkcode.location['y'])
        right = int(menu_frame_x+checkcode.location['x'] + checkcode.size['width'])
        bottom = int(menu_frame_y+checkcode.location['y'] + checkcode.size['height'])
        b.get_screenshot_as_file('checkcode.png')
        im = Image.open('checkcode.png')
        im2 = im.crop((left,top,right,bottom))
        im2.save('checkcode.png')
        left+=2
        global global_cnt
        checkcode = ""
        for i in range(4):
            im2 = im.crop((left,top,left+6,bottom))
            digit = self.ocr_digit(im2)
            checkcode = checkcode+str(digit)
            left += 10
        input_checkcodde.send_keys(checkcode)
        input_submit = b.find_element_by_xpath('//input[@name="Submit"]')
        input_submit.click()

def main():
    import json
    with open('config.json','r') as f:
        json_str = f.read()
    js = json.loads(json_str)
    while True:
        try:
            global global_cnt
            global_cnt+=1
            epc_order = EPC_orderer(js)
            epc_order.login()
            while True:
                try:
                    ret = epc_order.look_up_book_history()
                    if ret >= 4:
                        return
                    while True:
                        ret = epc_order.qiangke()
                        if ret == False:
                            break
                        else:
                            # epc_order.browser.get(epc_order.login_url)
                            pass
                except:
                    import traceback
                    epc_order.logger.warning(traceback.format_exc())
                    epc_order.browser.refresh()
        except KeyboardInterrupt:
            break
        except:
            import traceback
            traceback.print_exc()
    


        
        

if __name__ == '__main__':
    main()
    input("press enter to exit...")



